/**
 *@File Base64Code.cpp
 *
 *@Author: Bob
 *
 *@Create Time: 2016-12-5 13:23:22
 *
 *@Last Modify: Bob
 *
 *@Last Modify Time: 2016-12-5 13:23:22
 *
 *@Description:
 *   
 *
 */

#include "../include/Base64Code.h"


Base64Code::Base64Code()
{
	m_uEncodeBufferSize = MAX_BASE64CODE_BUFFER;
	m_uDecodeBufferSize = MAX_BASE64CODE_BUFFER;

	m_EncodeState.encode_step = Base64EncodeStep::EncodeStep_A;
	m_EncodeState.resultChar = 0;
	m_EncodeState.nStepCount = 0;

	m_DecodeState.decode_step = Base64DecodeStep::DecodeStep_A;
	m_DecodeState.plainChar = 0;
}

Base64Code::~Base64Code()
{

}


Base64Code* Base64Code::GetInstance()
{
	static Base64Code base64Code;
	return &base64Code;
}


bool Base64Code::InitEncodeState()
{
	m_EncodeState.encode_step = Base64EncodeStep::EncodeStep_A;
	m_EncodeState.resultChar = 0;
	m_EncodeState.nStepCount = 0;
}


std::string Base64Code::Encode()
{

}


std::string Base64Code::Decode()
{

}


bool Base64Code::EncodeFile(std::istream& istream_in, std::ostream& ostream_in)
{

}


bool Base64Code::DecodeFile(std::istream& istream_in, std::ostream& ostream_in)
{

}


char Base64Code::Base64EncodeValue(char value_in)
{
	static const char* pEncoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	char chRet = 0;
	if (value_in > 63)
	{
		chRet = '=';
	}
	else
	{
		chRet = pEncoding[(int)value_in];
	}

	return chRet;
}


int Base64Code::Base64EncodeBlock(const char* pPlaintext_in, int nLength_in, char* pCode_out, Base64EncodeState* pState_in)
{
	const char* pPlainchar = pPlaintext_in;
	const char* const pPlaintextEnd = pPlaintext_in + nLength_in;
	char* pCodechar = pCode_out;
	char resultChar;
	char fragmentChar;

	resultChar = m_EncodeState->resultChar;
	switch (m_EncodeState->encode_step)
	{
		while (1)
		{
			case Base64EncodeStep::EncodeStep_A:
			{
				if (pPlainchar == pPlaintextEnd)
				{
					m_EncodeState->resultChar = resultChar;
					m_EncodeState->encode_step = Base64EncodeStep::EncodeStep_A;
					return (pCodechar - pCode_out);
				}

				fragmentChar = *pPlainchar++;
				resultChar = (fragmentChar & 0x0fc) >> 2;
				*pCodechar++ = Base64EncodeValue(resultChar);
				resultChar = (fragmentChar & 0x003) << 4;
			}

			case Base64EncodeStep::EncodeStep_B:
			{
				if (pPlainchar == pPlaintextEnd)
				{
					m_EncodeState->resultChar = resultChar;
					m_EncodeState->nStepCount = Base64EncodeStep::EncodeStep_B;
					return (pCodechar - pCode_out);
				}
				fragmentChar = *pPlainchar++;
				resultChar |= (fragmentChar & 0x0f0) >> 4;
				*pCodechar++ = Base64EncodeValue(resultChar);
				resultChar = (fragmentChar & 0x00f) << 2;
			}

			case Base64EncodeStep::EncodeStep_C:
			{
				if (pPlainchar == pPlaintextEnd)
				{
					m_EncodeState->resultChar = resultChar;
					m_EncodeState->encode_step = Base64EncodeStep::EncodeStep_C;
					return (pCodechar - pCode_out);
				}

				fragmentChar = *pPlainchar++;
				resultChar |= (fragmentChar & 0x0c0) >> 6;
				*pCodechar++ = Base64EncodeValue(resultChar);
				resultChar = fragmentChar & 0x03f;
				*pCodechar++ = Base64EncodeValue(resultChar);

				++(m_EncodeState->nStepCount);
				if (m_EncodeState->nStepCount == 18)
				{
					*m_EncodeState++ = '\n';
					m_EncodeState->nStepCount = 0;
				}
			}
		}
	}

	return (pCodechar - pCode_out);
}


int Base64Code::Base64EncodeBlockEnd(char* pCode_out, Base64EncodeState* pState_in)
{
	char* pCodeChar = pCode_out;

	switch (m_EncodeState->encode_step)
	{
	case Base64EncodeStep::EncodeStep_B:
		{
			*pCodeChar++ = Base64EncodeValue(m_EncodeState->resultChar);
			*pCodeChar++ = '=';
			*pCodeChar++ = '=';
			break;
		}

	case Base64EncodeStep::EncodeStep_C:
		{
			*pCodeChar++ = Base64EncodeValue(m_EncodeState->resultChar);
			*pCodeChar++ = '=';
			break;
	    }

	case  Base64EncodeStep::EncodeStep_A:
		break;
	}
	pCodeChar++ = '\n';

	return (pCodeChar - pCode_out);
}


bool Base64Code::InitDecodeState()
{
	m_DecodeState.decode_step = Base64DecodeStep::DecodeStep_A;
	m_DecodeState.plainChar = 0;
}


int Base64Code::Base64DecodeValue(char value_in)
{
	static const char szDecoding[] = { 
		62, -1, -1, -1, 63,
		52, 53, 54, 55, 56,
		57, 58, 59, 60, 61,
		-1, -1, -1, -2, -1,
		-1, -1, 0, 1, 2,
		3, 4, 5, 6, 7,
		8, 9, 10, 11, 12,
		13, 14, 15, 16, 17,
		18, 19, 20, 21, 22,
		23, 24, 25, -1, -1,
		-1, -1, -1, -1, 26,
		27, 28, 29, 30, 31,
		32, 33, 34, 35, 36,
		37, 38, 39, 40, 41,
		42, 43, 44, 45, 46,
		47, 48, 49, 50, 51 };

	static const char decoding_size = sizeof(szDecoding);
	value_in -= 43;

	int nRet = -1;
	if (value_in < 0 || value_in > decoding_size)
	{
		nRet = -1
	}
	else
	{
		nRet = szDecoding[(int)value_in];
	}
	
	return nRet;
}


int Base64Code::Base64DecodeBlock(const char* pCode_in, int nLength_in, char* pPlaintext_out, Base64DecodeState* pState_in)
{
	const char* pCodechar = pCode_in;
	char* pPlainchar = pPlaintext_out;
	char fragmentChar;

	*pPlainchar = m_DecodeState->plainChar;

	switch (m_DecodeState->decode_step)
	{
		while (1)
		{
	        case Base64DecodeStep::DecodeStep_A:
	        {
				do 
				{
					if (pCodechar == (pCode_in + nLength_in))
					{
						m_DecodeState->decode_step = Base64DecodeStep::DecodeStep_A;
						m_DecodeState->plainChar = *pPlainchar;
						return (pPlainchar - pPlaintext_out);
					}
					fragmentChar = (char)Base64DecodeValue(*pCodechar++);
				} while (fragmentChar < 0);
				*pPlainchar = (fragmentChar & 0x03f) << 2;
			}

			case Base64DecodeStep::DecodeStep_B:
			{
				do 
				{
					if (pCodechar == (pCode_in + nLength_in))
					{
						m_DecodeState->decode_step = Base64DecodeStep::DecodeStep_B;
						m_DecodeState->plainChar = *pPlainchar;
						return (pPlainchar - pPlaintext_out);
					}
					fragmentChar = (char)Base64DecodeValue(*pCodechar++);
				} while (fragmentChar < 0);
				*pPlainchar++ |= (fragmentChar & 0x030) >> 4;
				*pPlainchar = (fragmentChar & 0x00f) << 4;
			}

			case Base64DecodeStep::DecodeStep_C:
			{
				do 
				{
					if (pCodechar = (pCode_in + nLength_in))
					{
						m_DecodeState->decode_step = Base64DecodeStep::DecodeStep_C;
						m_DecodeState->plainChar = *pPlainchar;
						return (pPlainchar - pPlaintext_out);
					}
					fragmentChar = (char)Base64DecodeValue(*pCodechar++);
				} while (fragmentChar < 0);
				*pPlainchar++ |= (fragmentChar & 0x03c) >> 2;
				*pPlainchar++ = (fragmentChar & 0x003) << 6;
			}

			case Base64DecodeStep::DecodeStep_D:
			{
				do 
				{
					if (pCodechar == (pCode_in + nLength_in))
					{
						m_DecodeState->decode_step = Base64DecodeStep::DecodeStep_D;
						m_DecodeState->plainChar = *pPlainchar;
						return (pPlainchar - pPlaintext_out);
					}
					fragmentChar = (char)Base64DecodeValue(*pCodechar++);
				} while (fragmentChar < 0);
				*pCodechar++ |= (fragmentChar & 0x03f);
			}
		}
	}
	return (pPlainchar - pPlaintext_out);
}